package com.atguigu.gmall.list.service.Impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.list.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author sunjipu
 * @create 2020-04-14 14:23
 */
@Service
public class ListServiceImpl implements ListService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;



    @Override
    public SearchResponseVo search(SearchParam searchParam) throws Exception {
        SearchRequest searchRequest = this.buildQueryDsl(searchParam);
        SearchResponse response = this.restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(response);

        SearchResponseVo responseVO = this.parseSearchResult(response);
        responseVO.setPageSize(searchParam.getPageSize());
        responseVO.setPageNo(searchParam.getPageNo());
        long totalPages = (responseVO.getTotal()+searchParam.getPageSize()-1)/searchParam.getPageSize();
        responseVO.setTotalPages(totalPages);
        return responseVO;
    }

    /**
     * 封装返回结果数据
     * @param response
     * @return
     * @throws JsonProcessingException
     */
    private SearchResponseVo parseSearchResult(SearchResponse response) throws JsonProcessingException {
        SearchResponseVo responseVO = new SearchResponseVo();
        // 获取总记录数
        SearchHits hits = response.getHits();
        responseVO.setTotal(hits.totalHits);
        // 解析品牌的聚合结果集
        // 获取品牌的聚合结果集
        Map<String, Aggregation> aggregationMap = response.getAggregations().asMap();
        ParsedLongTerms tmIdAgg = (ParsedLongTerms)aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> trademarkList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseTmVo trademark = new SearchResponseTmVo();
            // 获取品牌id
            trademark.setTmId(Long.parseLong(bucket.getKeyAsString()));
            // 获取品牌名称：通过子聚合来获取
            Map<String, Aggregation> tmIdSubMap = bucket.getAggregations().asMap();
            ParsedStringTerms tmNameAgg = (ParsedStringTerms)tmIdSubMap.get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            trademark.setTmName(tmName);
            return trademark;
        }).collect(Collectors.toList());
        responseVO.setTrademarkList(trademarkList);

        // 解析查询列表
        SearchHit[] subHits = hits.getHits();
        List<Goods> goodsList = new ArrayList<>();
        for (SearchHit subHit : subHits) {
            Goods goods = JSONObject.parseObject(subHit.getSourceAsString(), Goods.class);
            if(null != subHit.getHighlightFields().get("title")) {
                goods.setTitle(subHit.getHighlightFields().get("title").getFragments()[0].toString());
            }
            goodsList.add(goods);
        }
        responseVO.setGoodsList(goodsList);

        // 规格参数
        // 获取嵌套聚合对象
        ParsedNested attrAgg = (ParsedNested)aggregationMap.get("attrsAgg");
        // 规格参数id聚合对象
        ParsedLongTerms attrIdAgg = (ParsedLongTerms)attrAgg.getAggregations().get("attrIdAgg");
        List<Terms.Bucket> buckets = (List<Terms.Bucket>)attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(buckets)) {
            List<SearchResponseAttrVo> searchResponseAttrVOS = buckets.stream().map(bucket -> {
                SearchResponseAttrVo responseAttrVO = new SearchResponseAttrVo();
                // 设置规格参数id
                responseAttrVO.setAttrId(bucket.getKeyAsNumber().longValue());
                // 设置规格参数名
                List<? extends Terms.Bucket> nameBuckets = ((ParsedStringTerms) (bucket.getAggregations().get("attrNameAgg"))).getBuckets();
                responseAttrVO.setAttrName(nameBuckets.get(0).getKeyAsString());
                // 设置规格参数值的列表
                List<? extends Terms.Bucket> valueBuckets = ((ParsedStringTerms) (bucket.getAggregations().get("attrValueAgg"))).getBuckets();
                List<String> values = valueBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
                responseAttrVO.setAttrValueList(values);
                return responseAttrVO;
            }).collect(Collectors.toList());
            responseVO.setAttrsList(searchResponseAttrVOS);
        }


        return responseVO;
    }


    /**
     * 构建DSL
     * @param searchParam
     * @return
     */
    private SearchRequest buildQueryDsl(SearchParam searchParam){
        // 查询条件构建器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 1. 构建查询条件和过滤条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQueryBuilder);

        // 1.1. 构建查询条件
        // 1.1.1. 查询关键字
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword).operator(Operator.AND));
        }

        // 1.2. 构建过滤条件
        // 1.2.1.  构建品牌过滤
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = StringUtils.split(trademark, ":");
            if (split != null && split.length == 2) {
                boolQueryBuilder.filter(QueryBuilders.termsQuery("tmId", split[0]));
            }
        }

        // 1.2.2.  构建分类的过滤
        Long category1Id = searchParam.getCategory1Id();
        if (null != category1Id) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("category1Id", category1Id.toString()));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (null != category2Id) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("category2d", category2Id.toString()));
        }
        Long category3Id = searchParam.getCategory3Id();
        if (null != category3Id) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("category3Id", category3Id.toString()));
        }

        // 1.2.3.  构建规格属性嵌套过滤
//4:平台属性及属性值  平台属性ID:平台属性值:平台属性名称   2:6.75-6.84英寸:屏幕尺寸
        String[] props = searchParam.getProps();
        if (null != props && props.length > 0) {
            for (String prop : props) {
                BoolQueryBuilder subBoolQueryBuilder = QueryBuilders.boolQuery();
                //平台属性来循环
                String[] p = prop.split(":");
                if (null != p && p.length == 3) {
                    //组合对象
                    subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", p[0]));
                    subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", p[1]));
                }
                boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs", subBoolQueryBuilder, ScoreMode.None));
            }
        }


        // 2. 构建分页
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        sourceBuilder.from((pageNo - 1) * pageSize);
        sourceBuilder.size(pageSize);

        // 3. 构建排序
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = StringUtils.split(order, ":");
            if (split != null && split.length == 2) {
                String field = null;
                switch (split[0]) {
                    case "1": field = "hotScore"; break;
                    case "2": field = "price"; break;
                }
                sourceBuilder.sort(field, StringUtils.equals("asc", split[1]) ? SortOrder.ASC : SortOrder.DESC);
            }
        } else {
            sourceBuilder.sort("hotScore", SortOrder.DESC);
        }

        // 4. 构建高亮
        sourceBuilder.highlighter(new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>"));

        // 5. 构建聚合
        // 5.1.  品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName")));

        // 5.2.  搜索的规格属性聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrsAgg", "attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))));

        System.out.println(sourceBuilder.toString());

        // 结果集过滤
        sourceBuilder.fetchSource(new String[]{"id", "defaultImg", "title", "price"}, null);

        // 查询参数
        SearchRequest searchRequest = new SearchRequest("goods");
        searchRequest.types("info");
        searchRequest.source(sourceBuilder);
        return searchRequest;
    }
}